﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Web;
using Orchard;
using Orchard.Collections;
using Orchard.ContentManagement;
using Orchard.Core.Common.Models;
using Orchard.DisplayManagement;
using Orchard.Indexing;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Search.Models;
using Orchard.Search.Services;
using Orchard.Settings;
using Orchard.Themes;
using Orchard.UI.Admin;
using System.Web.Mvc;
using Orchard.UI.Navigation;
using Orchard.Utility.Extensions;
using SchoolPointe.Menus.Services;
using SchoolPointe.Menus.ViewModels;
using SchoolPointe.Menus.Models;
using SchoolPointe.Menus.Enums;
using SchoolPointe.RelatedContent.Services;
using SchoolPointe.RelatedContent.Models;
using Orchard.Autoroute.Models;
using Orchard.UI.Notify;

namespace SchoolPointe.Menus.Controllers
{
    [Admin]
    public class MenuItemAdminController : Controller, IUpdateModel
    {
        private readonly IOrchardServices _services;
        private readonly IContentManager _contentManager;
        private string _prefix;
        private IRelatedContentService _relatedContentService;
        private readonly IMenuService _menuService;
        private readonly ISiteService _siteService;
        private INotifier _notifier;

        public MenuItemAdminController(
            IOrchardServices services, 
            IContentManager contentManager, 
            IRelatedContentService relatedContentService, 
            IMenuService menuService, 
            ISiteService siteService,
            IShapeFactory shapeFactory,
            INotifier notifier)
        {
            _services = services;
            _contentManager = contentManager;
            _relatedContentService = relatedContentService;
            _menuService = menuService;
            _siteService = siteService;
            _notifier = notifier;
            T = NullLocalizer.Instance;
            Logger = NullLogger.Instance;
            Shape = shapeFactory;
        }

        public ILogger Logger { get; set; }
        public Localizer T { get; set; }
        private dynamic Shape { get; set; }

        [HttpPost]
        [Themed(false)]
        public ActionResult Create(string prefix, int menuId)
        {
            if (!Request.IsAjaxRequest())
                return new HttpUnauthorizedResult();

            _prefix = prefix;

            var newMenuItem = _contentManager.New<BetterMenuItemPart>("MenuItem");
            _contentManager.Create(newMenuItem);

            var editor = _contentManager.UpdateEditor(newMenuItem, this);

            var vm = _services.New.ViewModel();
            if (!ModelState.IsValid)
            {                
                _services.TransactionManager.Cancel();
                vm.Creator(_services.New.NestedEditor(
                    Editor: editor,
                    ParentPrefix: prefix));
            }
            else
            {
                var menuItemTemplate = _contentManager.New<BetterMenuItemPart>("MenuItem");
                menuItemTemplate.MenuItemType = newMenuItem.MenuItemType;

                vm.Creator(_services.New.NestedEditor(
                    Editor: _contentManager.BuildEditor(menuItemTemplate),
                    ParentPrefix: prefix + "." + DateTime.Now.Ticks.ToString()));

                prefix += "." + newMenuItem.Id.ToString();
                vm.Editor(_services.New.NestedEditor(
                    Editor: editor,
                    ParentPrefix: prefix,
                    DisplayType: "Edit"));
                
                var menu = _contentManager.Get<RelatedContentPart>(menuId);
                _relatedContentService.AddRelatedItem(newMenuItem.As<RelatedContentPart>(), "menuitem-parent", menu.ContentItem);
            }

            return View(vm);
        }

        [HttpPost]
        [Themed(false)]
        public ActionResult CreateContentItems(int menuId, int[] contentItemIds)
        {
            if (!Request.IsAjaxRequest())
                return new HttpUnauthorizedResult();

            var vm = _services.New.ViewModel();
            vm.Editors = new List<dynamic>();

            foreach (var contentItemId in contentItemIds)
            {
                var newMenuItem = _contentManager.New<BetterMenuItemPart>("MenuItem");
                var contentItem = _contentManager.Get(contentItemId);
                var menu = _contentManager.Get<RelatedContentPart>(menuId);

                newMenuItem.MenuItemType = MenuItemType.ContentItem;
                newMenuItem.SortOrder = 999;
                newMenuItem.Text = _contentManager.GetItemMetadata(contentItem).DisplayText;
                newMenuItem.Url = "http://updatethis.com/";
                _contentManager.Create(newMenuItem);

                
                _relatedContentService.AddRelatedItem(newMenuItem.As<RelatedContentPart>(), "menuitem-parent", menu.ContentItem);
                _relatedContentService.AddRelatedItem(newMenuItem.As<RelatedContentPart>(), "menu-content", contentItem);

                var editor = _services.New.NestedEditor(
                    Editor: _contentManager.BuildEditor(newMenuItem),
                    ParentPrefix: "RelatedContent.menu-items." + newMenuItem.Id.ToString(),
                    DisplayType: "Edit");
                vm.Editors.Add(editor);
            }            

            return View(vm);
        }

        [HttpPost]
        [Themed(false)]
        public ActionResult Edit(string prefix, int id)
        {
            if (!Request.IsAjaxRequest())
                return new HttpUnauthorizedResult();

            _prefix = prefix;

            var menuItem = _contentManager.Get<BetterMenuItemPart>(id);

            var editor = _contentManager.UpdateEditor(menuItem, this);

            var vm = _services.New.ViewModel();
            if (!ModelState.IsValid)            
                _services.TransactionManager.Cancel();

            vm.Editor(_services.New.NestedEditor(
                    Editor: editor,
                    ParentPrefix: prefix));
            vm.ParentPrefix = prefix;

            return View(vm);
        }

        [HttpPost]
        [Themed(false)]
        public ActionResult Delete(int id)
        {
            var ci = _contentManager.Get(id);
            _contentManager.Unpublish(ci);
            return Content("");
        }
        
        [HttpPost]
        [Themed(false)]
        public ActionResult UpdateParentAndSortOrder(int id, int parentId, Dictionary<int,int> updatedSortOrders)
        {
            var menuItem = _contentManager.Get<RelatedContentPart>(id);
            var oldParent = _relatedContentService.GetRelatedItems(menuItem, "menuitem-parent").Single();

            if (oldParent.Id != parentId)
                _relatedContentService.SetRelatedItems(menuItem, "menuitem-parent", new List<int> { parentId });

            foreach (var menuItemId in updatedSortOrders.Keys)
            {
                var sortedMenuItem = _contentManager.Get<BetterMenuItemPart>(menuItemId);
                sortedMenuItem.SortOrder = updatedSortOrders[menuItemId];
            }          

            return Content("");
        }

        [HttpGet]
        [Themed(false)]
        public ActionResult GetRecentContentByType(string[] types, int page = 1, int pageSize = 20)
        {
            if (types.Contains("*"))
                types = _menuService.GetPossibleContentTypes().Select(t => t.Name).ToArray();

            var pager = new Pager(_siteService.GetSiteSettings(), new PagerParameters() { Page = page, PageSize = pageSize });
            var query = _contentManager.Query(VersionOptions.Published, types)
                .ForPart<AutoroutePart>()
                .OrderByDescending<CommonPartRecord, DateTime?>(cr => cr.CreatedUtc);

            var pagerShape = Shape.Pager(pager).TotalItemCount(query.Count());
            var items = query.Slice(pager.GetStartIndex(), pager.PageSize)
                .Select(i =>
                    {
                        dynamic item = new ExpandoObject();
                        item.Id = i.Id;
                        item.Title = _contentManager.GetItemMetadata(i.ContentItem).DisplayText;
                        item.ContentItem = i;
                        return item;
                    }).ToList();
           
            dynamic viewModel = Shape.ViewModel()
                .ContentItems(items);

            if (pagerShape.TotalItemCount > pageSize)
                viewModel.Pager(pagerShape);

            return View("GetContentByType", viewModel);
        }

        [HttpGet]
        [Themed(false)]
        public ActionResult GetAllContentByType(string[] types, int page = 1, int pageSize = 20)
        {
            if (types.Contains("*"))
                types = _menuService.GetPossibleContentTypes().Select(t => t.Name).ToArray();

            var pager = new Pager(_siteService.GetSiteSettings(), new PagerParameters() { Page = page, PageSize = pageSize });
            var query = _contentManager.Query(VersionOptions.Published, types)
                .ForPart<AutoroutePart>()
                .OrderBy<CommonPartRecord, DateTime?>(cr => cr.CreatedUtc);

            var pagerShape = Shape.Pager(pager).TotalItemCount(query.Count());
            var items = query.Slice(pager.GetStartIndex(), pager.PageSize)
                .Select(i =>
                {
                    dynamic item = new ExpandoObject();
                    item.Id = i.Id;
                    item.Title = _contentManager.GetItemMetadata(i.ContentItem).DisplayText;
                    item.ContentItem = i;
                    return item;
                }).ToList();

            dynamic viewModel = Shape.ViewModel()
                .ContentItems(items);

            if (pagerShape.TotalItemCount > pageSize)
                viewModel.Pager(pagerShape);

            return View("GetContentByType", viewModel);
        }

        [HttpGet]
        [Themed(false)]
        public ActionResult SearchContentByType(string[] types, string q = "", int page = 0, int pageSize = 20)
        {
            if (types.Contains("*"))
                types = _menuService.GetPossibleContentTypes().Select(t => t.Name).ToArray();

            var pager = new Pager(_siteService.GetSiteSettings(), new PagerParameters() { Page = page, PageSize = pageSize });
            var searchFields = _services.WorkContext.CurrentSite.As<SearchSettingsPart>().SearchedFields;

            IPageOfItems<ISearchHit> searchHits = new PageOfItems<ISearchHit>(new ISearchHit[] { });
            try
            {

                searchHits = _menuService.SearchForContentItems(q, pager.Page, pager.PageSize,
                                                  searchFields,
                                                  types,
                                                  searchHit => searchHit);
            }
            catch (Exception exception)
            {
                Logger.Error(T("Invalid search query: {0}", exception.Message).Text);
                _notifier.Error(T("Invalid search query: {0}", exception.Message));
            }

            var list = new List<dynamic>();
            foreach (var contentItem in searchHits.Select(searchHit => _contentManager.Get(searchHit.ContentItemId)))
            {
                // ignore search results which content item has been removed or unpublished
                if (contentItem == null)
                {
                    searchHits.TotalItemCount--;
                    continue;
                }

                dynamic item = new ExpandoObject();
                item.Id = contentItem.Id;
                item.Title = _contentManager.GetItemMetadata(contentItem).DisplayText;
                item.ContentItem = contentItem;
                list.Add(item);
            }

            var pagerShape = Shape.Pager(pager).TotalItemCount(searchHits.TotalItemCount);

            dynamic viewModel = Shape.ViewModel()
                .ContentItems(list)
                .SearchTerm(q);

            if (pagerShape.TotalItemCount > pageSize)
                viewModel.Pager(pagerShape);

            return View(viewModel);
        }

        bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties)
        {
            if (!string.IsNullOrEmpty(_prefix))
                prefix = _prefix + "." + prefix;

            return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
        }

        void IUpdateModel.AddModelError(string key, LocalizedString errorMessage)
        {
            ModelState.AddModelError(key, errorMessage.ToString());
        }       
    }
}